package com.ziiwee.reduce.rest.condition.parser;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ziiwee.reduce.commons.utils.BeanUtil;
import com.ziiwee.reduce.rest.condition.Condition;
import com.ziiwee.reduce.rest.condition.Conditions;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;

/**
 * DefaultConditionParser
 *
 * @author ziiwee
 * @date 2020/5/29
 */
public class DefaultQLConditionParser extends AbstractQLConditionParser {

    protected static final List<String> OR_SYMBOLS = Arrays.asList("/", "|", "||");
    protected static final List<String> AND_SYMBOLS = Arrays.asList("&", "&&");
    protected static final List<String> BETWEEN_SYMBOLS = Arrays.asList("~", "-");
    protected static final List<String> LT_SYMBOLS = Arrays.asList("<");
    protected static final List<String> LE_SYMBOLS = Arrays.asList("<=", "=<");
    protected static final List<String> GT_SYMBOLS = Arrays.asList(">");
    protected static final List<String> GE_SYMBOLS = Arrays.asList(">=", "=>");
    protected static final List<String> EQ_SYMBOLS = Arrays.asList("=");
    protected static final List<String> NE_SYMBOLS = Arrays.asList("!=", "><", "<>");
    protected static final List<String> LIKE_SYMBOLS = Arrays.asList("*");
    protected static final List<String> NULL_SYMBOLS = Arrays.asList("?");
    protected static final List<String> NOT_NULL_SYMBOLS = Arrays.asList("!?");
    protected static final List<String> IN_SYMBOLS = Arrays.asList(":");

    public DefaultQLConditionParser(Class<?> modelType) {
        super(modelType);
    }

    protected Condition matchSymbol(List<String> symbols, String ql, BiFunction<String, String, Condition> handler) {
        for (String symbol : symbols) {
            int indexOf = ql.indexOf(symbol);
            if (indexOf >= 0) {
                String left = ql.substring(0, indexOf).trim();
                if (StringUtils.hasLength(left)) {
                    String right = ql.substring(indexOf + symbol.length()).trim();
                    return handler.apply(left, right);
                } else {
                    throw new ConditionParserException("无法解析的表达式：" + ql);
                }
            }
        }
        return null;
    }

    protected Condition matchSymbolMultipleTimes(List<String> symbols, String ql, Function<List<String>, Condition> handler) {
        for (String symbol : symbols) {
            if (ql.contains(symbol)) {
                List<String> qls = new ArrayList<>();
                while (ql.contains(symbol)) {
                    int indexOf = ql.indexOf(symbol);
                    String left = ql.substring(0, indexOf).trim();
                    if (StringUtils.hasLength(left)) {
                        qls.add(left);
                    }
                    ql = ql.substring(indexOf + symbol.length()).trim();
                }
                if (StringUtils.hasLength(ql)) {
                    qls.add(ql);
                }
                return handler.apply(qls);
            }
        }
        return null;
    }


    protected Comparable castComparable(String name, String value) {
        return (Comparable) castValue(name, value);
    }

    /**
     * 转换字段值类型
     *
     * @param name  字段名
     * @param value 字段值
     * @return
     */
    protected Object castValue(String name, String value) {
        try {
            value = reductionStringValue(value.trim());
            Object castValue = new ObjectMapper().readValue(value, Objects.requireNonNull(BeanUtil.getField(modelType, name)).getType());
            if (castValue != null) {
                return castValue;
            }
            throw new ConditionParserException("无法解析的值：" + value);
        } catch (Exception e) {
            throw new ConditionParserException(String.format("无法解析的值: %s :%s", value, e.getMessage()), e);
        }
    }

    @Override
    protected Condition parseLogicCondition(String ql) {
        Condition condition = matchSymbolMultipleTimes(OR_SYMBOLS, ql, subQls -> {
            List<Condition> list = new ArrayList<>();
            for (String subQl : subQls) {
                if (StringUtils.hasLength(subQl)) {
                    list.add(parse(subQl));
                }
            }
            return Conditions.or(list);
        });
        if (condition != null) {
            return condition;
        }
        return matchSymbolMultipleTimes(AND_SYMBOLS, ql, subQls -> {
            List<Condition> list = new ArrayList<>();
            for (String subQl : subQls) {
                if (StringUtils.hasLength(subQl)) {
                    list.add(parse(subQl));
                }
            }
            return Conditions.and(list);
        });
    }

    @SuppressWarnings("unchecked")
    @Override
    protected Condition parseBetweenCondition(String ql) {
        return matchSymbol(BETWEEN_SYMBOLS, ql, (left, right) -> {
            right = reductionStringValue(right);
            String[] values = right.split(",");
            return Conditions.between(left, castComparable(left, values[0]), castComparable(left, values[1]));
        });
    }

    @SuppressWarnings("unchecked")
    @Override
    protected Condition parseComparableCondition(String ql) {
        Condition condition = matchSymbol(LE_SYMBOLS, ql, (left, right) -> Conditions.lte(left, castComparable(left, right)));
        if (condition != null) {
            return condition;
        }
        condition = matchSymbol(GE_SYMBOLS, ql, (left, right) -> Conditions.gte(left, castComparable(left, right)));
        if (condition != null) {
            return condition;
        }
        condition = matchSymbol(LT_SYMBOLS, ql, (left, right) -> Conditions.lt(left, castComparable(left, right)));
        if (condition != null) {
            return condition;
        }
        return matchSymbol(GT_SYMBOLS, ql, (left, right) -> Conditions.gt(left, castComparable(left, right)));
    }

    @Override
    protected Condition parseEqualCondition(String ql) {
        Condition condition = matchSymbol(EQ_SYMBOLS, ql, (left, right) -> Conditions.eq(left, castComparable(left, right)));
        if (condition != null) {
            return condition;
        }
        return matchSymbol(NE_SYMBOLS, ql, (left, right) -> Conditions.ne(left, castComparable(left, right)));
    }

    @Override
    protected Condition parseInCondition(String ql) {
        return matchSymbol(IN_SYMBOLS, ql, (left, right) -> {
            right = reductionStringValue(right);
            String[] values = right.split(",");
            List<Object> in = new ArrayList<>();
            for (String value : values) {
                in.add(castValue(left, value.trim()));
            }
            return Conditions.in(left, in);
        });
    }

    @Override
    protected Condition parseLikeCondition(String ql) {
        return matchSymbol(LIKE_SYMBOLS, ql, (left, right) -> Conditions.like(left, reductionStringValue(right)));
    }

    @Override
    protected Condition parseNullCondition(String ql) {
        Condition condition = matchSymbol(NOT_NULL_SYMBOLS, ql, (left, right) -> Conditions.isNotNull(left));
        matchSymbol(NULL_SYMBOLS, ql, (left, right) -> Conditions.isNull(left));
        if (condition != null) {
            return condition;
        }
        return matchSymbol(NULL_SYMBOLS, ql, (left, right) -> Conditions.isNull(left));
    }
}
